package ru.usu.gv.uicomponents;

import com.sun.j3d.utils.geometry.Text2D;
import com.sun.j3d.utils.picking.PickTool;
import com.sun.j3d.utils.universe.ViewingPlatform;

import ru.usu.gv.core.GraphCanvas;
import ru.usu.gv.styles.StyleFacade;
import ru.usu.gv.utils.GeometryUtil;
import ru.usu.gv.utils.splines.SplineUtilities;

import javax.media.j3d.Billboard;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Node;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import java.awt.Font;
import java.util.List;

/**
	@author Sergey Pupyrev 
	26.10.2008
*/
public class UIUtilities
{
	public static void makePickable(UIElementBase element, Shape3D shape)
	{
		shape.setUserData(element);
		shape.setCapability(Node.ENABLE_PICK_REPORTING);

		try
		{
			PickTool.setCapabilities(shape, PickTool.INTERSECT_FULL);
		}
		catch (javax.media.j3d.RestrictedAccessException e)
		{
		}
	}

	public static BranchGroup addVertexLabel(String label, BranchGroup labelBranch)
	{
		if (label != null && label.length() > 0)
		{
			double scale = 0.2;//10d / Math.log(getViewItem().getSize() * 10 * Math.E);
			Color3f c = new Color3f(0.0f, 0.0f, 0.0f);
			Text2D text2D = new Text2D(label, c, "Dummy", 30, Font.PLAIN);
			TransformGroup textTG = new TransformGroup();
			textTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
			textTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
			textTG.addChild(text2D);
			BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
			Billboard billboard = new Billboard(textTG, Billboard.ROTATE_ABOUT_POINT, new Point3f(0.0f, 0.05f, -0.07f));
			billboard.setSchedulingBounds(bounds);
			TransformGroup l = new TransformGroup();
			Transform3D scaleTransform = new Transform3D();
			scaleTransform.setScale(scale);
			l.setTransform(scaleTransform);
			l.addChild(billboard);
			l.addChild(textTG);
			if (labelBranch != null)
			{
				labelBranch.detach();
			}
			labelBranch = new BranchGroup();
			labelBranch.setCapability(BranchGroup.ALLOW_DETACH);
			Transform3D translation = new Transform3D();
			translation.setTranslation(new Vector3f(-0.01f * label.length() / 2, 0.03f, 0.0f));
			TransformGroup translateTG = new TransformGroup(translation);
			translateTG.addChild(l);
			labelBranch.addChild(translateTG);
			return labelBranch;
		}
		else
		{
			if (labelBranch != null)
			{
				labelBranch.detach();
			}
			return null;
		}
	}

	private static final double HIT_DISTANCE = 150.0d;

	//returns array [controlPoints, draggedIndex]
	public static Object[] getDraggedControlPointIndex(TransformGroup tg, Point3d[] coordinates, Point3d[] controlPoints, Point2d hitPoint)
	{
		Point2d[] control2d = new Point2d[coordinates.length];
		for (int i = 0; i < coordinates.length; i++)
			control2d[i] = GeometryUtil.vworldToCanvasPosition(new Point3d(coordinates[i]), tg);

		double minDist = Double.MAX_VALUE;
		int minIndex = -1;
		for (int i = SplineUtilities.SEGMENT_SIZE + 1; i < control2d.length - 1; i += SplineUtilities.SEGMENT_SIZE)
		{
			double curDist = control2d[i].distance(hitPoint);
			if (curDist < minDist)
			{
				minDist = curDist;
				minIndex = i / SplineUtilities.SEGMENT_SIZE - 1;
			}
		}

		if (minDist < HIT_DISTANCE)
		{
			System.out.println("hit: '" + minDist + "'");
			return new Object[] { minIndex, controlPoints };
		}
		else
		{
			System.out.println("loose: '" + minDist + "'");
			minDist = Double.MAX_VALUE;
			minIndex = -1;
			for (int i = 0; i < coordinates.length - 1; i++)
			{
				double curDist = control2d[i].distance(hitPoint) + control2d[i + 1].distance(hitPoint);
				if (curDist < minDist)
				{
					minDist = curDist;
					minIndex = i;
				}
			}

			if (minIndex == -1)
			{
				System.out.println("something wrong");
			}

			double k = control2d[minIndex + 1].distance(control2d[minIndex]) / hitPoint.distance(control2d[minIndex]);

			Vector3d diff = new Vector3d(coordinates[minIndex + 1]);
			diff.sub(coordinates[minIndex]);
			diff.normalize();
			diff.scale(k);

			Point3d newPoint = new Point3d(coordinates[minIndex]);
			newPoint.add(diff);

			int insertIndex = (minIndex >= 1 ? (minIndex - 1) / SplineUtilities.SEGMENT_SIZE : 0);
			if (insertIndex >= controlPoints.length)
				insertIndex = controlPoints.length - 1;

			Point3d[] newControlPoints = new Point3d[controlPoints.length + 1];
			arraycopy(controlPoints, 0, newControlPoints, 0, insertIndex + 1);
			newControlPoints[insertIndex + 1] = newPoint;

			arraycopy(controlPoints, insertIndex + 1, newControlPoints, insertIndex + 2, controlPoints.length - insertIndex - 1);
			System.out.println("Insert index: " + insertIndex);

			return new Object[] { insertIndex + 1, newControlPoints };
		}
	}

	public static void fitGraphIntoWindow()
	{
		UIGraph uiGraph = GraphCanvas.getInstance().getCurrentGraph();
		if (uiGraph.getVertexes() == null || uiGraph.getVertexes().isEmpty())
			return;

		Transform3D xyTransform = new Transform3D();
		GraphCanvas.getInstance().getTransformGroup().getTransform(xyTransform);
		ViewingPlatform viewingPlatform = GraphCanvas.getInstance().getUniverse().getViewingPlatform();

		double maxZ = 2.41;
		for (UIVertex vertex : uiGraph.getVertexes())
		{
			Point3d p = vertex.getPosition();
			Point3d pNew = new Point3d();
			xyTransform.transform(p, pNew);

			maxZ = Math.max(maxZ, Math.abs(pNew.x) / 0.35 + Math.abs(pNew.z));
			maxZ = Math.max(maxZ, Math.abs(pNew.y) / 0.25 + Math.abs(pNew.z));
		}

		Transform3D transform3D = new Transform3D();
		viewingPlatform.getViewPlatformTransform().getTransform(transform3D);

		Matrix4d trans = new Matrix4d();
		transform3D.get(trans);

		trans.m23 = maxZ;

		transform3D.set(trans);
		viewingPlatform.getViewPlatformTransform().setTransform(transform3D);
	}

	public static void scaleWindow(double scale)
	{
		Transform3D transform3D = new Transform3D();
		TransformGroup viewPlatformTransform = GraphCanvas.getInstance().getUniverse().getViewingPlatform().getViewPlatformTransform();
		viewPlatformTransform.getTransform(transform3D);

		Matrix4d trans = new Matrix4d();
		transform3D.get(trans);

		trans.m23 += scale;

		transform3D.set(trans);
		viewPlatformTransform.setTransform(transform3D);
	}

	public static void centralizeGraph()
	{
		UIGraph uiGraph = GraphCanvas.getInstance().getCurrentGraph();
		List<UIVertex> vertexes = uiGraph.getVertexes();
		if (vertexes.isEmpty())
			return;

		Point3d[] points = new Point3d[vertexes.size()];
		for (int i = 0; i < points.length; i++)
			points[i] = vertexes.get(i).getPosition();

		GeometryUtil.centralize(points);
		uiGraph.draw();

/*		for (UIVertex v : uiGraph.getVertexes())
		{
			double dist = v.getPosition().distance(new Point3d());
			System.out.println(v.getVertex().getEdgesSize() + "     " + dist);
		}*/
	}

	public static void scaleGraph()
	{
		UIGraph uiGraph = GraphCanvas.getInstance().getCurrentGraph();
		List<UIEdge> edges = uiGraph.getEdges();
		if (edges.isEmpty())
			return;

		double sum = 0, cnt = 0;
		for (UIEdge edge : edges)
		{
			double length = edge.getLength();
			if (length > StyleFacade.getEdgeLength() / 10.0)
			{
				sum += length;
				cnt++;
			}
		}

		if (cnt == 0)
			return;
		sum /= cnt;
		double scale = sum / StyleFacade.getEdgeLength();

		for (UIVertex vertex : uiGraph.getVertexes())
		{
			vertex.getPosition().scale(1.0 / scale);
		}

		uiGraph.draw();
	}

	private static void arraycopy(Point3d[] src, int srcPos, Point3d[] dest, int destPos, int len)
	{
		for (int i = 0; i < len; i++)
			dest[destPos + i] = new Point3d(src[srcPos + i]);
	}

}
